package tw.com.hitevision.whiteboard.android.widgets;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.text.InputType;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.gif.GifDrawable;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import androidx.annotation.ColorInt;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import tw.com.hitevision.whiteboard.android.Props;
import tw.com.hitevision.whiteboard.android.R;

public class SearchImageView extends RelativeLayout implements ImageViewAdapter.ImageClickListener {
    private static final String TAG = SearchImageView.class.getSimpleName();
    //    private final static String SEARCH_HINT_TEXT = "Search anything you want!";
    private final static String MSG_SEARCH_CANCEL = "Touch again to cancel the current search.";
    public final static int INVALID_POSITION = -9999;
    private @ColorInt
    int focusedSearchTextColor;
    private @ColorInt
    int unfocusedSearchTextColor;
    private ModifiedEditTextForSkia searchInput;
    private Context context;
    private View searchButton;
    private RecyclerView recyclerView;
    //    private RecyclerView.Adapter adapter;
    private ImageViewAdapter adapter;
    private RecyclerView.LayoutManager layoutManager;
    private List<String> dataSet;
    private List<String> srcImageUrls;
    private List<float[]> imageInfo;
    private SearchViewListener searchViewListener;
    private boolean isDraggingDisable;
    private ImageView imagePreview;
    private View touchBlock;
    private View progressBar;
    private boolean forceCancel;
    private TextView searchViewNotification;
    private boolean scrollingToBottom;
    private String keyword;
    private long lastSearchTime;

    public SearchImageView(Context context) {
        super(context);
        initialize(context);
        this.context = context;
    }

    // Android creates this object by using this constructor.
    public SearchImageView(Context context, AttributeSet attributes) {
        super(context, attributes);
        initialize(context);
        this.context = context;
    }

    public SearchImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initialize(context);
        this.context = context;
    }

    @SuppressLint("ClickableViewAccessibility")
    private void initialize(Context context) {
        inflate(context, R.layout.menu_search, this);

        searchInput = findViewById(R.id.searchInput);
        searchInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES);
        focusedSearchTextColor = getResources().getColor(R.color.dark_gray);
        unfocusedSearchTextColor = getResources().getColor(R.color.light_gray);
        isDraggingDisable = false;

        if (Props.getFlavorModel().equals(Props.Flavor.Model.Oem)) {
            setBackgroundColor(getResources().getColor(R.color.oem_theme_color));
            searchInput.setTextColor(getResources().getColor(R.color.white));
            focusedSearchTextColor = getResources().getColor(R.color.white);
        }

//        searchInput.setOnFocusChangeListener((v, hasFocus) -> {
//            if (hasFocus) {
//                searchInput.setTextColor(focusedSearchTextColor);
//            } else {
//                hideKeyboard();
//            }
//        });

        searchInput.setOnEditorActionListener((v, actionId, event) -> {
            if (actionId == EditorInfo.IME_ACTION_SEARCH || actionId == EditorInfo.IME_ACTION_DONE || actionId == EditorInfo.IME_ACTION_GO) {
                searchImage(searchInput.getText().toString());
            }
            return false;
        });

        searchInput.setUsingInSearchImage(true);
        searchInput.setOnFocusChangeListener(new OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                scrollingToBottom = hasFocus;
            }
        });

        searchButton = findViewById(R.id.searchButton);
        searchButton.setOnClickListener(searchClickLis);
        searchInput.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                if (actionId == EditorInfo.IME_ACTION_SEARCH) {
                    searchButton.callOnClick();
                }
                return false;
            }
        });

        recyclerView = findViewById(R.id.recyclerView);
        recyclerView.addItemDecoration(new SimpleItemDecorator(5, true));

        // use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView
        recyclerView.setHasFixedSize(true);

        // use a grid layout manager
        layoutManager = new GridLayoutManager(context, 3) {
            @Override
            public boolean canScrollVertically() {
                //Similarly you can customize "canScrollHorizontally()" for managing horizontal scroll
                // return true to enable scrolling
                return !isDraggingDisable && super.canScrollVertically();
            }
        };
        recyclerView.setLayoutManager(layoutManager);

        // specify an adapter
        dataSet = new ArrayList<>();
        srcImageUrls = new ArrayList<>();
        imageInfo = new ArrayList<>();
        adapter = new ImageViewAdapter(dataSet, imageInfo, context, this);
        recyclerView.setAdapter(adapter);

//        imagePreview = findViewById(R.id.image_preview);

        recyclerView.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (searchInput.isFocused()) {
                    searchInput.clearFocus();
                    hideKeyboard();
                }
                return false;
            }
        });

        // fix the problem that recyclerView does not scroll when keyboard is opened.
//        recyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
//            @Override
//            public void onGlobalLayout() {
//
//                Rect r = new Rect();
//                recyclerView.getWindowVisibleDisplayFrame(r);
//                int screenHeight = recyclerView.getRootView().getHeight();
//
//                // r.bottom is the position above soft keypad or device button.
//                // if keypad is shown, the r.bottom is smaller than that before.
//                int keypadHeight = screenHeight - r.bottom;
//
//                if (keypadHeight > screenHeight * 0.15) { // 0.15 ratio is perhaps enough to determine keypad height.
//                    // keyboard is opened
//                    if (!scrollingToBottom) {
//                        scrollingToBottom = true;
//                        RecyclerView.Adapter adapter = recyclerView.getAdapter();
//                        if (adapter != null && adapter.getItemCount() > 0) {
//                            recyclerView.scrollToPosition(adapter.getItemCount() - 1);
//                        }
//                    }
//                }
//                else {
//                    // keyboard is closed
//                    scrollingToBottom = false;
//                }
//            }
//        });

        searchViewNotification = findViewById(R.id.searchNotification);

        forceCancel = false;

        touchBlock = findViewById(R.id.touch_block);
        touchBlock.setVisibility(GONE);
        touchBlock.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                int action = event.getAction();
                if (searchViewListener != null) {
                    if ((action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN)) {
                        if (forceCancel) {
                            searchViewListener.cancelSearchTask();
                        } else {
                            forceCancel = true;
                            searchViewNotification.setVisibility(VISIBLE);
                            searchViewNotification.setText(MSG_SEARCH_CANCEL);
                        }
                    }
                }
                return true;
            }
        });

        progressBar = findViewById(R.id.search_progress_bar);
        progressBar.setVisibility(GONE);

//        this.setOnTouchListener(rootOnTouchListener);

        recyclerView.setItemAnimator(null);

        View root = findViewById(R.id.relativeLayout);
        root.setOnTouchListener((v, event) -> {
            if (searchInput.isFocused()) {
                searchInput.clearFocus();
            }
            hideKeyboard();
            return true;
        });
    }

    @Override
    public void setVisibility(int visibility) {
        super.setVisibility(visibility);
        if (imagePreview != null && visibility == GONE) {
            imagePreview.setVisibility(GONE);
        }

        if (visibility == VISIBLE && searchInput != null) {
            searchInput.setText("");
        }

        if (visibility == GONE) {
            searchInput.clearFocus();
            hideKeyboard();
        }
    }

    public void setImagePreview(ImageView view) {
        imagePreview = view;
    }

    public class SimpleItemDecorator extends RecyclerView.ItemDecoration {

        int space;
        boolean isHorizontalLayout;

        public SimpleItemDecorator(int space) {
            this.space = space;
        }

        public SimpleItemDecorator(int space, boolean isHorizontalLayout) {
            this.space = space;
            this.isHorizontalLayout = isHorizontalLayout;
        }

        @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
            super.getItemOffsets(outRect, view, parent, state);
            if (isHorizontalLayout) {
                outRect.bottom = space;
                outRect.right = space;
                outRect.left = space;
                outRect.top = space;

            } else {
                outRect.bottom = space;
                if (parent.getChildAdapterPosition(view) == 0) {
                    outRect.top = space;
                } else {
                    outRect.top = 0;
                }

            }
        }
    }

    private final OnClickListener searchClickLis = new OnClickListener() {
        @Override
        public void onClick(View v) {
            if (v.getId() == R.id.searchButton && searchInput.getText().length() > 0 && searchInput.getCurrentTextColor() == focusedSearchTextColor) {
                keyword = searchInput.getText().toString();
                if (searchInput.isFocused()) {
                    searchInput.clearFocus();
                    hideKeyboard();
                }
                searchImage(searchInput.getText().toString());
            }
        }
    };

    private void searchImage(String queryStr) {
        if (queryStr.length() <= 0) {
            return;
        }

        long currentTime = System.currentTimeMillis();
        if (currentTime - lastSearchTime < 500) {
            return;
        }

        lastSearchTime = currentTime;

        // refresh dataSet
        if (dataSet != null) {
            if (searchViewListener != null) {
                Glide.get(context).clearMemory();

                dataSet.clear();
                srcImageUrls.clear();
                imageInfo.clear();

                adapter.notifyDataSetChanged();

                touchBlock.setVisibility(VISIBLE);
                progressBar.setVisibility(VISIBLE);

                adapter.resetCurrentSelectedIndex();

                forceCancel = false;
                searchViewNotification.setVisibility(GONE);

                searchViewListener.getImageSet(dataSet, queryStr);
            }
        }
    }

    public void updateSearchResult(List<String> tbnUrls, List<String> srcUrls, List<float[]> size, String errorMsg) {
        boolean error = false;
        if (tbnUrls == null || srcUrls == null || size == null || tbnUrls.size() != srcUrls.size() || tbnUrls.size() == 0 || srcUrls.size() == 0) {
            log("Error when updateSearchResult : " + (tbnUrls == null) + " | " + (srcUrls == null) + " | " + (size == null) +
                    " | thumbnail size : " + (tbnUrls != null ? tbnUrls.size() : "null") + " | srcUrls size : " + (srcUrls != null ? srcUrls.size() : "null"));
            error = true;
        } else {
            // clear previous result
            // bitmap is a kind of native object. You don't need to recycle it manually, GC will deal this.

            if (dataSet.size() > 0 || srcImageUrls.size() > 0 || imageInfo.size() > 0) {
                dataSet.clear();
                srcImageUrls.clear();
                imageInfo.clear();
            }

            dataSet.addAll(tbnUrls);
            srcImageUrls.addAll(srcUrls);
            imageInfo.addAll(size);

            // reset to top position
//            recyclerView.getLayoutManager().scrollToPosition(0);
        }


        if (!error) {
            adapter.notifyDataSetChanged();

            // Update the recyclerView layout if the keyboard is not the floating keyboard.
            SearchImageView.this.requestLayout();

            if (keyword != null && keyword.length() > 0) {
                searchInput.setText(keyword);
            }
        }
        touchBlock.setVisibility(GONE);
        progressBar.setVisibility(GONE);

        forceCancel = false;

        if (errorMsg == null || errorMsg.isEmpty()) {
            searchViewNotification.setVisibility(GONE);
        } else {
            searchViewNotification.setVisibility(VISIBLE);
            searchViewNotification.setText(errorMsg);
        }

    }

    public void setTouchBlock(boolean lock) {
        if (lock) {
            touchBlock.setVisibility(VISIBLE);
            progressBar.setVisibility(VISIBLE);
        } else {
            touchBlock.setVisibility(GONE);
            progressBar.setVisibility(GONE);
            searchViewNotification.setVisibility(GONE);
        }
    }

    private void hideKeyboard() {
        InputMethodManager imm = (InputMethodManager) context.getSystemService(Activity.INPUT_METHOD_SERVICE);
        if (imm != null) {
            imm.hideSoftInputFromWindow(searchInput.getWindowToken(), 0);
        }
    }

    public void toggleWebView(boolean toggle) {
        if (toggle) {
            this.setVisibility(View.VISIBLE);
        } else {
            searchInput.clearFocus();
            hideKeyboard();
            this.setVisibility(View.GONE);
        }
    }

    public boolean backKeyHandle() {
        searchInput.clearFocus();
        hideKeyboard();
        this.setVisibility(View.GONE);
        return false;
    }

    @Override
    public void createImageByClick(ImageView v, int id) {
        Drawable d = v.getDrawable();
        if (d != null && d.getIntrinsicWidth() >= 0 && d.getIntrinsicHeight() >= 0 && ((BitmapDrawable) d).getBitmap() != null) {
            Bitmap b = ((BitmapDrawable) d).getBitmap();
            if (b != null) {
                if (searchViewListener != null && id >= 0 && id < srcImageUrls.size() && id < dataSet.size()
                        && srcImageUrls.get(id) != null && !srcImageUrls.get(id).isEmpty()) {
                    // Do not use Bitmap.createBitmap, createBitmap will try to recycle bitmap if this bitmap is as same as the bitmap in pool.
                    // That means u just create a reference to this bitmap, not really create a new bitmap.
                    touchBlock.setVisibility(VISIBLE);
                    progressBar.setVisibility(VISIBLE);
//                searchViewListener.createImage(b.copy(b.getConfig(), false), srcImageUrls.get(id));
                    searchViewListener.createImage(dataSet.get(id), srcImageUrls.get(id), INVALID_POSITION, INVALID_POSITION);
                }
            }
        }
    }

    @Override
    public void createImageByDragging(ImageView v, int id, float x, float y) {
        x += (this.getX() + recyclerView.getX());
        y += (this.getY() + recyclerView.getY());

        if (x <= recyclerView.getRight() || y > recyclerView.getBottom()) {
            return;
        }

        Drawable d = v.getDrawable();
        Bitmap b;

        if (d instanceof GifDrawable) {
            return;
        } else {
            b = ((BitmapDrawable) d).getBitmap();
        }

        if (b != null) {
            if (searchViewListener != null && srcImageUrls.get(id) != null && srcImageUrls.get(id).length() > 0) {
                // Do not use Bitmap.createBitmap, createBitmap will try to recycle bitmap if this bitmap is as same as the bitmap in pool.
                // That means u just create a reference to this bitmap, not really create a new bitmap.
                touchBlock.setVisibility(VISIBLE);
                progressBar.setVisibility(VISIBLE);
//                searchViewListener.createImage(b.copy(b.getConfig(), false), srcImageUrls.get(id), x, y);
                searchViewListener.createImage(dataSet.get(id), srcImageUrls.get(id), x, y);
            }
        }
    }

    @Override
    public void hideKeyBoardFromAdapter() {
        if (searchInput.isFocused()) {
            searchInput.clearFocus();
            hideKeyboard();
        }
    }

    @Override
    public void disableDragging(boolean disable) {
        isDraggingDisable = disable;
    }

    @Override
    public void setPreview(ImageView v) {
        imagePreview.setVisibility(GONE);

        if (v != null) {
            Drawable d = v.getDrawable();
            imagePreview.setImageDrawable(d);

            imagePreview.getLayoutParams().width = v.getWidth();
            imagePreview.getLayoutParams().height = v.getHeight();
            imagePreview.setX(v.getX());
            imagePreview.setY(v.getY());
        } else {
            imagePreview.setImageDrawable(null);
        }
    }

    @Override
    public void movePreview(ImageView v, float x, float y) {
        if (imagePreview.getVisibility() != VISIBLE) {
            imagePreview.setVisibility(VISIBLE);
        }
        imagePreview.setX(recyclerView.getX() + x);
        imagePreview.setY(recyclerView.getY() + y);
        imagePreview.invalidate();
    }

    public void setSearchViewListener(SearchViewListener lis) {
        this.searchViewListener = lis;
    }

    private @ColorInt
    int getRandomColor() {
        Random r = new Random();
        return Color.rgb(r.nextInt(256), r.nextInt(256), r.nextInt(256));
    }

    private int getRandonInt(int min, int max) {
        return min + (int) (Math.random() * ((max - min) + 1));
    }

    private void log(String str) {
        Log.d(TAG, str);
    }

    public interface SearchViewListener {
        //        void createImage(Bitmap bitmap, String url);
//        void createImage(Bitmap bitmap, String url, float x, float y);
        void createImage(String thumbnailUrl, String sourceUrl, float x, float y);

        void getImageSet(List<String> dataSet, String searchTerm);

        void cancelSearchTask();
    }
}
